make_unique
and make_shared
are more concise than explicitly calling the constructor of unique_ptr
and
shared_ptr
since they don’t require specifying the type multiple times and eliminate the need to use new
.
make_unique
and make_shared
should also be preferred for exception-safety and performance reasons.
Exception-Safety
While make_unique
and make_shared
are exception-safe, complex constructions of unique_ptr
and
shared_ptr
might not be because C++ allows arbitrary order of evaluation of subexpressions (until C++17).
Consider this example:
f(unique_ptr<Lhs>(new Lhs()), throwingFunction());
The following scenario can happen:
- Memory allocation for
Lhs
- Construction of the
Lhs
object
- Call to
throwingFunction
(before the unique_ptr
construction)
-
throwingFunction
throws an exception
- The constructed
Lhs
object is leaked since the unique_ptr
isn’t constructed yet
Note: This scenario can only happen before C++17. Since C++17, the standard states that even though the order of evaluation of each argument is
still unspecified, interleaving the evaluation of different arguments is no longer allowed. This makes the direct construction of
unique_ptr
and shared_ptr
exception-safe.
Performance
Using make_unique()
doesn’t impact performance, but make_shared()
improves it slightly.
Indeed, constructing
explicitly a shared_ptr()
requires two heap allocations: one for the managed object and the other for the control block that stores data
about the ref-counts and the shared_ptr()
deleter. make_shared()
on the other hand, performs only one heap allocation.
Note: Because make_shared
performs only one allocation for both the object and the control block, the memory occupied by the object
will be deallocated when no shared_ptr
or weak_ptr
points to it. If the object is large, a weak_ptr
is used,
and memory is a concern, explicitly calling the constructor of shared_ptr
may be preferred. This way, the object’s memory will be
deallocated when there are no more shared owners, independently of any weak_ptr
s.
Noncompliant code example
std::unique_ptr<MyClass> uniqueP(new MyClass(42)); // Noncompliant
std::shared_ptr<MyClass> sharedP(new MyClass(42)); // Noncompliant
Compliant solution
auto uniqueP = std::make_unique<MyClass>(42);
auto sharedP = std::make_shared<MyClass>(42);
Exceptions
This rule ignores code that uses features not supported by make_shared
and make_unique
:
std::unique_ptr<std::FILE, std::function<void(std::FILE*)>> file(
fopen("example.txt", "r"),
[](FILE* inFile) { fclose(inFile); }); // Compliant: custom deleter is specified
- calling placement-new, i.e., version of
new
with arguments, like new(std::nothrow)
In addition, make_shared
does not support the following:
- custom operator
new
- allocating arrays (before C++20)